Friday, May 7, 2021

Long Live the SSH Ramdisk

A few weeks back a friend ran into a strange little bug that ended up corrupting some relatively unimportant system files on their phone. Those files were important enough that iOS wouldn't boot as long as the files remained corrupted, but unimportant enough that simply deleting them would cause iOS to generate them anew. The problem was, the phone wouldn't boot, so we couldn't access those files to delete them. So begins our journey.

One of my first thoughts when I realized what had happened was of this little project called ssh-rd by msftguy. For those who might not know, it was a really useful tool back in the day for essentially live-booting a small OS that you could SSH into (using limera1n). Offline filesystem access would be a perfect fix for this scenario.

I googled and found two similar projects that appear to work with checkm8 devices. We have SSH-Ramdisk-Maker-and-Loader by Ralph0045, and telnetd_ramdisk by danieltroger. Neither project worked for me out of the box (I'll spare the details, see issues page on both projects). I also found this somewhat related guide. While neither project worked for me, they proved the idea was possible and the source code pointed me in the right direction.

Rather than develop and release another tool, I'll try to walk through the steps for doing this manually and provide a couple of scripts that will help you along the way. This isn't really for the faint of heart. You will need to compile a lot of tools, and what worked for me may not work for you. This guide assumes a 64-bit device, though it could be modified to work for 32-bit.

At the end of this guide, I'll also provide a few scripts that can be used as a quick-start point that will hopefully save someone a bit of time.

My setup:

  • Mac running 10.15 (Catalina)
  • iPhone 8 running 13.5

Dependencies

  • A device vulnerable to checkm8 (A11 processor or earlier)
  • Your IPSW
    • While it will need to be for the correct device, this doesn't need to be the version you have installed. This is the IPSW we will use as a base image to boot. I used 13.5, I've heard it works well for other people too. iOS 14 may not work for you here. You might need to try multiple versions and see what works.
  • An APTicket for your device.
    • This does not need to be for any specific version. I used a 13.7 ticket.
    • You can extract one from a saved SHSH2 blob.
  • Some keys from theiphonewiki.
    • These should match the firmware of your IPSW.
  • XCode command line tools
    • xcode-select --install
  • The iPhone SDK for your firmware
    • We need this to compile restored_external
    • You don't need to be a developer to download the SDKs anymore
    • Drop it in /Library/Developer/CommandLineTools/SDKs/
    • If you can't compile it you can try my binary, but it probably won't work
  • img4 via img4lib
  • kairos
  • Kernel64Patcher
  • irecovery via libirecovery
  • compareFiles.py
    • curl 'https://raw.githubusercontent.com/dualbootfun/dualbootfun.github.io/d947e2c9b6090a1e65a46ea6a58cd840986ff9d9/source/compareFiles.py' | sed -n '3,$p' > ./bin/compareFiles.py
  • ldid2 via xerub's ldid fork
  • iproxy via libusbmuxd
  • PyBoot - An easy way to pwn DFU, though not the only way
  • Something that can extract a deb file (I use 7zip)
  • Some dropbear resources
    • A launch daemon
    • A host key
      • Note: normally it's a bad idea to reuse host keys, but in this case, it's not going to be a problem since the interface will only ever be accessible over USB. If you want to generate your own dropbear host key, though, that's not a problem.
  • restored_external.c
  • A couple more things we'll wget along the way

Setting up

First we've got to decrypt and then sign a few things from the IPSW.
  1. Extract your IPSW and put the relevant contents into a folder called "resources". You will know which resources are relevant because they will be listed for your device / firmware in theiphonewiki. Not all the files in your ipsw will be relevant.
    7z x ./iPhone_4.7_P3_13.5_17F75_Restore.ipsw
    1. Restore ramdisk (you don't need the update ramdisk or root filesystem)
    2. Restore ramdisk trustcache
    3. DeviceTree
    4. applelogo
    5. iBEC
    6. iBSS
    7. kernelcache
  2. Add your SHSH2 blob to the "resources" folder

Building the bootchain

This phase will be three steps. Not all items will go through each step. For "IV+KEY", you'll just concatenate the IV and the KEY (in that order) found for the relevant file on theiphonewiki. If your file has a key listed on the iphonewiki, use it, otherwise leave the key blank.
  1. Decrypt - We'll store the decrypted files in the "decrypted" folder. Be sure to use IVs and keys from theiphonewiki as necessary.
    mkdir ./decrypted/
    img4 -i ./resources/iBSS* -o ./decrypted/iBSS.dec -k IV+KEY
    img4 -i ./resources/iBEC* -o ./decrypted/iBEC.dec -k IV+KEY
    img4 -i ./resources/applelogo* -o ./decrypted/applelogo.dec
    img4 -i ./resources/DeviceTree* -o ./decrypted/devicetree.dec
    img4 -i ./resources/kernelcache* -o ./decrypted/kernelcache.dec
    img4 -i ./resources/*dmg -o ./decrypted/ramdisk.dec.dmg
  2. Patch - Patch a few things and put them in the "patched" folder. We generate a binary patch for the kernelcache to use later.
    mkdir ./patched/
    kairos ./decrypted/iBSS.dec ./patched/iBSS.patched
    kairos ./decrypted/iBEC.dec ./patched/iBEC.patched -b "rd=md0 -v"
    Kernel64Patcher ./decrypted/kernelcache.dec ./patched/kernelcache.patched -a
    ./bin/compareFiles.py ./decrypted/kernelcache.dec ./patched/kernelcache.patched # > ./kc.bpatch
  3. Sign - Extract the apticket from the shsh2 file, and then use it to sign everything.
    mkdir ./rd_image/
    plutil -extract ApImg4Ticket xml1 -o - ./resources/*.shsh2 | xmllint -xpath '/plist/data/text()' - | base64 -D > ./apticket.der
    img4 -i ./patched/iBSS.patched -o ./rd_image/iBSS.img4 -T ibss -A -M ./apticket.der
    img4 -i ./patched/iBEC.patched -o ./rd_image/iBEC.img4 -T ibec -A -M ./apticket.der
    img4 -i ./decrypted/applelogo.dec -o ./rd_image/applelogo.img4 -T logo -A -M ./apticket.der
    img4 -i ./decrypted/devicetree.dec -o ./rd_image/devicetree.img4 -T rdtr -A -M ./apticket.der
    img4 -i ./resources/kernelcache* -o ./rd_image/kernelcache.img4 -T rkrn -P ./kc.bpatch -J -M ./apticket.der
    img4 -i ./resources/*trustcache -o ./rd_image/trustcache -M ./apticket.der

Build the ramdisk

Essentially the idea here is that we're going to create a sort of 'skel' directory with a structure that matches the ramdisk. It will contain all the files we want to use when we've booted (binaries, ssh server, etc). Then, we'll copy that skel directory over the top of the ramdisk. We also need to expand the ramdisk so that we'll have space to add those files. Finally, we sign it with our apticket just like the rest of the images.
  1. Create ramdisk skel, populate it with some binaries and paths
    mkdir ./rd_skel/
    cd ./rd_skel/
    wget -q 'http://newosxbook.com/tools/binpack64-256.tar.gz'
    tar xzf ./binpack64-256.tar.gz
    rm -f ./binpack64-256.tar.gz
    mkdir -p ./var/root/
    chmod +x ./usr/bin/*
  2. Install ncurses libs
    wget -q 'https://apt.bingner.com/debs/1443.00/ncurses_6.1+20181013-1_iphoneos-arm.deb'
    7z x ./ncurses*.deb > /dev/null
    rm ./ncurses*.deb
    tar xf ./data.tar 'usr/lib/'
    rm ./data.tar
    cd ./usr/lib/
    ln -s ./libncurses.6.dylib libncurses.5.4.dylib
    cd ../../../
  3. Add dropbear resources
    mkdir -p ./rd_skel/System/Library/LaunchDaemons/
    mkdir -p ./rd_skel/etc/dropbear/
    wget -q 'https://gist.githubusercontent.com/compilingEntropy/60e84d15bc274f88b6f53e6c3788e8e9/raw/597355e51831cafef8751b598c15832bac5fdd9d/dropbear.plist' -O ./rd_skel/System/Library/LaunchDaemons/dropbear.plist
    wget -q 'https://gist.githubusercontent.com/compilingEntropy/f7042cfb1f402c6eff0afb14014cefe1/raw/c7d73ad466a3e8af7828a58a5be0c6f490b68f3e/id_rsa' -O - | base64 -d > ./rd_skel/etc/dropbear/id_rsa
    wget -q 'https://gist.githubusercontent.com/compilingEntropy/ff0a80f156a135f7c386598a44ba8bb3/raw/bd594fcb4d252de3c41a25a267c4350cb70078b9/motd' -O ./rd_skel/etc/motd
  4. Resize and create ramdisk
    cp -a ./decrypted/*.dec.dmg ./ramdisk.dmg
    hdiutil resize -size 150MB ./ramdisk.dmg
    mkdir ./mnt/
    hdiutil attach -mountpoint ./mnt/ ./ramdisk.dmg
  5. Build / sign restored_external, add it to ramdisk
    mv ./mnt/usr/local/bin/restored_external{,_original}
    wget -q 'https://gist.githubusercontent.com/compilingEntropy/3c6f19f85cdce53fdf44b7b84005023d/raw/c0057abfe00eba3971a9c0a1d296e07dde23467c/restored_external.c' -O ./resources/restored_external.c
    xcrun -sdk iphoneos clang -arch arm64 ./resources/restored_external.c -o ./mnt/usr/local/bin/restored_external
    ldid2 -S ./mnt/usr/local/bin/restored_external
  6. Apply our modifications
    rsync --ignore-existing -ahuK ./rd_skel/ ./mnt/
  7. Sign and pack the ramdisk
    hdiutil unmount ./mnt/
    rmdir ./mnt/
    img4 -i ./ramdisk.dmg -o ./rd_image/ramdisk -T rdsk -A -M ./apticket.der
  8. Cleanup
    rm ./ramdisk.dmg
    rm -rf ./rd_skel/

Booting the ramdisk

Here, we'll pwn DFU mode and then send our images to the device with irecovery. We'll also send some commands to the device, usually to tell it to load the images.
  1. Enter DFU mode
  2. pwn DFU
    cd ./bin/PyBoot/
    ./pyboot.py -p
  3. Send the files / commands with irecovery. Sometimes it helps to wait a second or two between sending images to give the device a chance to finish loading them.
    echo 'sending ibss (to jump to recovery mode)'
    irecovery -f ./iBSS.img4
    echo 'sending ibss again'
    irecovery -f ./iBSS.img4
    echo 'sending ibec'
    irecovery -f ./iBEC.img4
    echo 'loading ibec'
    irecovery -c go
    echo 'sending ramdisk'
    irecovery -f ./ramdisk
    echo 'loading ramdisk'
    irecovery -c ramdisk
    echo 'sending logo'
    irecovery -f ./applelogo.img4
    echo 'loading logo'
    irecovery -c 'setpicture 5'
    echo 'sending devicetree'
    irecovery -f ./devicetree.img4
    echo 'loading devicetree'
    irecovery -c devicetree
    echo 'sending trustcache'
    irecovery -f ./trustcache
    echo 'validating firmware against trustcache'
    irecovery -c firmware
    echo 'sending kernel'
    irecovery -f ./kernelcache.img4
    echo 'booting now'
    irecovery -c bootx
You should see your screen display the apple logo, followed by a verbose boot. Then, the device will display an apple logo with a loading bar. This is when you can connect to the device. After a few minutes, the screen will go dark again. This doesn't make a difference to your connection.

Connecting

iproxy lets us make network connections over usb. We'll use it to forward port 22 on the device to port 2222 on our client. From there, you simply need to SSH to the device.
  1. iproxy
    iproxy 2222:22 &
  2. SSH - password is alpine
    ssh root@127.0.0.1 -p 2222
  3. When you're done, simply:
    reboot

Wrapping up

While there are some limitations to what we can do while connected to a device with an SSH ramdisk, this can be extremely useful for resolving certain issues. In the case of my friend, deleting a few corrupt files was all that was needed for the device to boot right up again!

I do want to mention that this process is untested on iOS 14. iOS 14 introduced a "feature" where the phone is able to detect whether a device was booted from DFU or not, and if it finds that it has been, the device will reboot. If you test this with iOS 14, please report your findings in the comments or let me know on twitter and I'll update this post.

Lastly, I'll include a few scripts you can use as a template to speed this process along. If they don't say "done" after running them, something has failed along the way and you'll have to debug that. These scripts aren't necessarily "out of the box"—you will have to modify them to fit your setup. With those caveats in mind, this should be a good starting point if you want to automate this process.

8 comments:

  1. Hi, I've been trying to get this working, with multiple devices (iPhone 5s, iPad 2 mini) when I load the Ramdisk it hangs at 13.3% and never gets too 100%. The rest of the process seems to continue and work fine but I feel that the hang is what is causing this not to succeed. If I change the Ramdisk to a smaller size i.e. 100MB instead of 150 the loading bar makes it to 20%. Does recovery have an issue with loading large files? Or have you seen this before? Thanks!

    ReplyDelete
    Replies
    1. Becoz u need to edit your script on booting ramdisk

      Delete
  2. Hi, Please contact me ASAP at my telegram www.t.me/ADAKS_MEDIA . I will pay for your time.

    ReplyDelete
  3. 你的邮箱方便给我吧

    ReplyDelete
  4. 看到的邮箱我会回复你的

    ReplyDelete
  5. kex_exchange_identification: read: Connection reset by peer
    Connection reset by 127.0.0.1 port 2222

    ReplyDelete
  6. It works with ios 15.5,but it can not mount the system files via ssh.

    ReplyDelete
  7. It works with Sierra.

    ReplyDelete